/*
 * This file is part of TimeCounter.
 * A static non-reentrant library which acts as interface
 * to the high-resolution hardware counter.
 * Copyright (C) 2013 Giulio Franco
 *         
 * This program is free software: you can redistribute it and/or modify
 * it under the terms of the GNU General Public License as published by
 * the Free Software Foundation, either version 3 of the License, or
 * (at your option) any later version.
 * 
 * This program is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 * GNU General Public License for more details.
 * 
 * You should have received a copy of the GNU General Public License
 * along with this program.  If not, see <http://www.gnu.org/licenses/>.
 */

#include "time_mgmt.h"
#include <time.h>

#ifdef _WIN32

#include <windows.h>

static
LARGE_INTEGER last_time;
static
LARGE_INTEGER pcres = {0};

int timer_resolution(double* resolution)
{
    int res;
    if ((res = QueryPerformanceFrequency(&pcres)) && resolution)
    {
		*resolution = 1.0 / (double)pcres.QuadPart;
    }
    return !res;
}

#define TAGT(type) \
    LARGE_INTEGER pcval;\
    int res;\
    pcval = last_time;\
    if ((res = QueryPerformanceCounter(&last_time)))\
    {\
        pcval.QuadPart = (pcval.QuadPart <= last_time.QuadPart) ?\
                (last_time.QuadPart - pcval.QuadPart) : \
                (last_time.QuadPart - pcval.QuadPart + 0x7FFFFFFFFFFFFFl);\
        if (pcres.QuadPart == 0) timer_resolution(NULL);\
        *output = (type) pcval.QuadPart / (type) pcres.QuadPart;\
    }\
    return !res;\

int tick_and_get_f(float* output)
{
    TAGT(float)
}

int tick_and_get_d(double* output)
{
    TAGT(double)
}

int tick()
{
    LARGE_INTEGER pcval;
    return !QueryPerformanceCounter(&last_time);
}

#undef TAGT

#else

static
struct timespec last_time = {0,0};

int timer_resolution(double* resolution)
{
    int res;
    struct timespec hrt_res;
    if (!(res = clock_getres(CLOCK_MONOTONIC_RAW, &hrt_res)) && resolution)
    {
        *resolution = (hrt_res.tv_nsec / 1e9) + hrt_res.tv_sec;
    }
    return res;
}

#define TAGT(type) \
    struct timespec hrt_val;\
    int res;\
    hrt_val = last_time;\
    if (!(res = clock_gettime(CLOCK_MONOTONIC_RAW, &last_time)))\
    {\
        *output = ((last_time.tv_nsec - hrt_val.tv_nsec) / 1e9) + (last_time.tv_sec - hrt_val.tv_sec);\
    }\
    return res;\

int tick_and_get_f(float* output)
{
    TAGT(float)
}

int tick_and_get_d(double* output)
{
    TAGT(double)
}

int tick()
{
    return clock_gettime(CLOCK_MONOTONIC_RAW, &last_time);
}

#undef TAGT

#endif
